--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Examples/Channel.py 85d77c10a11ff0e943b100ef86fb5cbfa791c8b3 (85d77c10) Text, 13.43 KB
T8b949e##########################################################
T8b949e# This RNS example demonstrates how to set up a link to #
T8b949e# a destination, and pass structured messages over it #
T8b949e# using a channel. #
T8b949e##########################################################
Tff7b72import T7ee787os
Tff7b72import T7ee787sys
Tff7b72import T7ee787time
Tff7b72import T7ee787argparse
Tff7b72from T7ee787datetime Tff7b72import Te6edf3datetime
Tff7b72import T7ee787RNS
Tff7b72from T7ee787RNST7ee787.T7ee787vendor Tff7b72import Te6edf3umsgpack
T8b949e# Let's define an app name. We'll use this for all
T8b949e# destinations we create. Since this echo example
T8b949e# is part of a range of example utilities, we'll put
T8b949e# them all within the app namespace "example_utilities"
Te6edf3APP_NAME Tff7b72= Ta5d6ff"Ta5d6ffexample_utilitiesTa5d6ff"
T8b949e##########################################################
T8b949e#### Shared Objects ######################################
T8b949e##########################################################
T8b949e# Channel data must be structured in a subclass of
T8b949e# MessageBase. This ensures that the channel will be able
T8b949e# to serialize and deserialize the object and multiplex it
T8b949e# with other objects. Both ends of a link will need the
T8b949e# same object definitions to be able to communicate over
T8b949e# a channel.
T8b949e#
T8b949e# Note: The objects we wish to use over the channel must
T8b949e# be registered with the channel, and each link has a
T8b949e# different channel instance. See the client_connected
T8b949e# and link_established functions in this example to see
T8b949e# how message types are registered.
T8b949e# Let's make a simple message class called StringMessage
T8b949e# that will convey a string with a timestamp.
Tff7b72class T56d364StringMessageTb4b4b4(Te6edf3RNSTff7b72.Td2a8ffMessageBaseTb4b4b4)Tb4b4b4:
T8b949e# The MSGTYPE class variable needs to be assigned a
T8b949e# 2 byte integer value. This identifier allows the
T8b949e# channel to look up your message's constructor when a
T8b949e# message arrives over the channel.
T8b949e#
T8b949e# MSGTYPE must be unique across all message types we
T8b949e# register with the channel. MSGTYPEs >= 0xf000 are
T8b949e# reserved for the system.
Te6edf3MSGTYPE Tff7b72= T79c0ff0x0101
T8b949e# The constructor of our object must be callable with
T8b949e# no arguments. We can have parameters, but they must
T8b949e# have a default assignment.
T8b949e#
T8b949e# This is needed so the channel can create an empty
T8b949e# version of our message into which the incoming
T8b949e# message can be unpacked.
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffdata Tff7b72= Te6edf3data
Tff7b72selfTff7b72.Td2a8fftimestamp Tff7b72= Te6edf3datetimeTff7b72.Td2a8ffnowTb4b4b4(Tb4b4b4)
T8b949e# Finally, our message needs to implement functions
T8b949e# the channel can call to pack and unpack our message
T8b949e# to/from the raw packet payload. We'll use the
T8b949e# umsgpack package bundled with RNS. We could also use
T8b949e# the struct package bundled with Python if we wanted
T8b949e# more control over the structure of the packed bytes.
T8b949e#
T8b949e# Also note that packed message objects must fit
T8b949e# entirely in one packet. The number of bytes
T8b949e# available for message payloads can be queried from
T8b949e# the channel using the Channel.MDU property. The
T8b949e# channel MDU is slightly less than the link MDU due
T8b949e# to encoding the message header.
T8b949e# The pack function encodes the message contents into
T8b949e# a byte stream.
Tff7b72def Td2a8ffpackTb4b4b4(Tff7b72selfTb4b4b4) Tff7b72-Tff7b72> Tffa657bytesTb4b4b4:
Tff7b72return Te6edf3umsgpackTff7b72.Td2a8ffpackbTb4b4b4(Tb4b4b4(Tff7b72selfTff7b72.Td2a8ffdataTb4b4b4, Tff7b72selfTff7b72.Td2a8fftimestampTb4b4b4)Tb4b4b4)
T8b949e# And the unpack function decodes a byte stream into
T8b949e# the message contents.
Tff7b72def Td2a8ffunpackTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3rawTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffdataTb4b4b4, Tff7b72selfTff7b72.Td2a8fftimestamp Tff7b72= Te6edf3umsgpackTff7b72.Td2a8ffunpackbTb4b4b4(Te6edf3rawTb4b4b4)
T8b949e##########################################################
T8b949e#### Server Part #########################################
T8b949e##########################################################
T8b949e# A reference to the latest client link that connected
Te6edf3latest_client_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a server
Tff7b72def Td2a8ffserverTb4b4b4(Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Randomly create a new identity for our link example
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTb4b4b4(Tb4b4b4)
T8b949e# We create a destination that clients can connect to. We
T8b949e# want clients to create links to this destination, so we
T8b949e# need to create a "single" destination type.
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffINTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffchannelexampleTa5d6ff"
Tb4b4b4)
T8b949e# We configure a function that will get called every time
T8b949e# a new client creates a link to this destination.
Te6edf3server_destinationTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3client_connectedTb4b4b4)
T8b949e# Everything's ready!
T8b949e# Let's Wait for client requests or user input
Te6edf3server_loopTb4b4b4(Te6edf3server_destinationTb4b4b4)
Tff7b72def Td2a8ffserver_loopTb4b4b4(Te6edf3destinationTb4b4b4)Tb4b4b4:
T8b949e# Let the user know that everything is ready
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffChannel example Ta5d6ff"Tff7b72+
Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tff7b72+
Ta5d6ff"Ta5d6ff running, waiting for a connection.Ta5d6ff"
Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffHit enter to manually send an announce (Ctrl-C to quit)Ta5d6ff"Tb4b4b4)
T8b949e# We enter a loop that runs until the users exits.
T8b949e# If the user hits enter, we will announce our server
T8b949e# destination on the network, which will let clients
T8b949e# know how to create messages directed towards it.
Tff7b72while Tff7b72TrueTb4b4b4:
Te6edf3entered Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
Te6edf3destinationTff7b72.Td2a8ffannounceTb4b4b4(Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffSent announce from Ta5d6ff"Tff7b72+Te6edf3RNSTff7b72.Td2a8ffprettyhexrepTb4b4b4(Te6edf3destinationTff7b72.Td2a8ffhashTb4b4b4)Tb4b4b4)
T8b949e# When a client establishes a link to our server
T8b949e# destination, this function will be called with
T8b949e# a reference to the link.
Tff7b72def Td2a8ffclient_connectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3latest_client_link
Te6edf3latest_client_link Tff7b72= Te6edf3link
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient connectedTa5d6ff"Tb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3client_disconnectedTb4b4b4)
T8b949e# Register message types and add callback to channel
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3channelTff7b72.Td2a8ffregister_message_typeTb4b4b4(Te6edf3StringMessageTb4b4b4)
Te6edf3channelTff7b72.Td2a8ffadd_message_handlerTb4b4b4(Te6edf3server_message_receivedTb4b4b4)
Tff7b72def Td2a8ffclient_disconnectedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffClient disconnectedTa5d6ff"Tb4b4b4)
Tff7b72def Td2a8ffserver_message_receivedTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4:
T8b949e"""
A message handler
@param message: An instance of a subclass of MessageBase
@return: True if message was handled
"""
Tff7b72global Te6edf3latest_client_link
T8b949e# When a message is received over any active link,
T8b949e# the replies will all be directed to the last client
T8b949e# that connected.
T8b949e# In a message handler, any deserializable message
T8b949e# that arrives over the link's channel will be passed
T8b949e# to all message handlers, unless a preceding handler indicates it
T8b949e# has handled the message.
T8b949e#
T8b949e#
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3StringMessageTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff" Tff7b72+ Te6edf3messageTff7b72.Td2a8ffdata Tff7b72+ Ta5d6ff"Ta5d6ff (message created at Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3messageTff7b72.Td2a8fftimestampTb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3reply_message Tff7b72= Te6edf3StringMessageTb4b4b4(Ta5d6ff"Ta5d6ffI received Tffea00\"Ta5d6ff"Tff7b72+Te6edf3messageTff7b72.Td2a8ffdataTff7b72+Ta5d6ff"Tffea00\"Ta5d6ff over the linkTa5d6ff"Tb4b4b4)
Te6edf3latest_client_linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffsendTb4b4b4(Te6edf3reply_messageTb4b4b4)
T8b949e# Incoming messages are sent to each message
T8b949e# handler added to the channel, in the order they
T8b949e# were added.
T8b949e# If any message handler returns True, the message
T8b949e# is considered handled and any subsequent
T8b949e# handlers are skipped.
Tff7b72return Tff7b72True
T8b949e##########################################################
T8b949e#### Client Part #########################################
T8b949e##########################################################
T8b949e# A reference to the server link
Te6edf3server_link Tff7b72= Tff7b72None
T8b949e# This initialisation is executed when the users chooses
T8b949e# to run as a client
Tff7b72def Td2a8ffclientTb4b4b4(Te6edf3destination_hexhashTb4b4b4, Te6edf3configpathTb4b4b4)Tb4b4b4:
T8b949e# We need a binary representation of the destination
T8b949e# hash that was entered on the command line
Tff7b72tryTb4b4b4:
Te6edf3dest_len Tff7b72= Tb4b4b4(Te6edf3RNSTff7b72.Td2a8ffReticulumTff7b72.Td2a8ffTRUNCATED_HASHLENGTHTff7b72/Tff7b72/T79c0ff8Tb4b4b4)Tff7b72*T79c0ff2
Tff7b72if Tffa657lenTb4b4b4(Te6edf3destination_hexhashTb4b4b4) Tff7b72!= Te6edf3dest_lenTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffDestination length is invalid, must be Tffd700{hex}Ta5d6ff hexadecimal characters (Tffd700{byte}Ta5d6ff bytes).Ta5d6ff"Tff7b72.Td2a8ffformatTb4b4b4(Tffa657hexTff7b72=Te6edf3dest_lenTb4b4b4, Te6edf3byteTff7b72=Te6edf3dest_lenTff7b72/Tff7b72/T79c0ff2Tb4b4b4)
Tb4b4b4)
Te6edf3destination_hash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3destination_hexhashTb4b4b4)
Tff7b72exceptTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffInvalid destination entered. Check your input!Tffea00\nTa5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# We must first initialise Reticulum
Te6edf3reticulum Tff7b72= Te6edf3RNSTff7b72.Td2a8ffReticulumTb4b4b4(Te6edf3configpathTb4b4b4)
T8b949e# Check if we know a path to the destination
Tff7b72if Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffDestination is not yet known. Requesting path and waiting for announce to arrive...Ta5d6ff"Tb4b4b4)
Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffrequest_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)
Tff7b72while Tff7b72not Te6edf3RNSTff7b72.Td2a8ffTransportTff7b72.Td2a8ffhas_pathTb4b4b4(Te6edf3destination_hashTb4b4b4)Tb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
T8b949e# Recall the server identity
Te6edf3server_identity Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentityTff7b72.Td2a8ffrecallTb4b4b4(Te6edf3destination_hashTb4b4b4)
T8b949e# Inform the user that we'll begin connecting
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffEstablishing link with server...Ta5d6ff"Tb4b4b4)
T8b949e# When the server identity is known, we set
T8b949e# up a destination
Te6edf3server_destination Tff7b72= Te6edf3RNSTff7b72.Td2a8ffDestinationTb4b4b4(
Te6edf3server_identityTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffOUTTb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffDestinationTff7b72.Td2a8ffSINGLETb4b4b4,
Te6edf3APP_NAMETb4b4b4,
Ta5d6ff"Ta5d6ffchannelexampleTa5d6ff"
Tb4b4b4)
T8b949e# And create a link
Te6edf3link Tff7b72= Te6edf3RNSTff7b72.Td2a8ffLinkTb4b4b4(Te6edf3server_destinationTb4b4b4)
T8b949e# We'll also set up functions to inform the
T8b949e# user when the link is established or closed
Te6edf3linkTff7b72.Td2a8ffset_link_established_callbackTb4b4b4(Te6edf3link_establishedTb4b4b4)
Te6edf3linkTff7b72.Td2a8ffset_link_closed_callbackTb4b4b4(Te6edf3link_closedTb4b4b4)
T8b949e# Everything is set up, so let's enter a loop
T8b949e# for the user to interact with the example
Te6edf3client_loopTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ffclient_loopTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72global Te6edf3server_link
T8b949e# Wait for the link to become active
Tff7b72while Tff7b72not Te6edf3server_linkTb4b4b4:
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.1Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72False
Tff7b72while Tff7b72not Te6edf3should_quitTb4b4b4:
Tff7b72tryTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3text Tff7b72= Tffa657inputTb4b4b4(Tb4b4b4)
T8b949e# Check if we should quit the example
Tff7b72if Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffquitTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffqTa5d6ff" Tff7b72or Te6edf3text Tff7b72== Ta5d6ff"Ta5d6ffexitTa5d6ff"Tb4b4b4:
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# If not, send the entered text over the link
Tff7b72if Te6edf3text Tff7b72!= Ta5d6ff"Ta5d6ff"Tb4b4b4:
Te6edf3message Tff7b72= Te6edf3StringMessageTb4b4b4(Te6edf3textTb4b4b4)
Te6edf3packed_size Tff7b72= Tffa657lenTb4b4b4(Te6edf3messageTff7b72.Td2a8ffpackTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3channel Tff7b72= Te6edf3server_linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3channelTff7b72.Td2a8ffis_ready_to_sendTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3packed_size Tff7b72<Tff7b72= Te6edf3channelTff7b72.Td2a8ffmduTb4b4b4:
Te6edf3channelTff7b72.Td2a8ffsendTb4b4b4(Te6edf3messageTb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(
Ta5d6ff"Ta5d6ffCannot send this packet, the data size of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3packed_sizeTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytes exceeds the link packet MDU of Ta5d6ff"Tff7b72+
Tffa657strTb4b4b4(Te6edf3channelTff7b72.Td2a8ffMDUTb4b4b4)Tff7b72+Ta5d6ff"Ta5d6ff bytesTa5d6ff"Tb4b4b4,
Te6edf3RNSTff7b72.Td2a8ffLOG_ERROR
Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffChannel is not ready to send, please wait for Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffpending messages to complete.Ta5d6ff"Tb4b4b4, Te6edf3RNSTff7b72.Td2a8ffLOG_ERRORTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffError while sending data over the link: Ta5d6ff"Tff7b72+Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4)
Te6edf3should_quit Tff7b72= Tff7b72True
Te6edf3server_linkTff7b72.Td2a8ffteardownTb4b4b4(Tb4b4b4)
T8b949e# This function is called when a link
T8b949e# has been established with the server
Tff7b72def Td2a8fflink_establishedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
T8b949e# We store a reference to the link
T8b949e# instance for later use
Tff7b72global Te6edf3server_link
Te6edf3server_link Tff7b72= Te6edf3link
T8b949e# Register messages and add handler to channel
Te6edf3channel Tff7b72= Te6edf3linkTff7b72.Td2a8ffget_channelTb4b4b4(Tb4b4b4)
Te6edf3channelTff7b72.Td2a8ffregister_message_typeTb4b4b4(Te6edf3StringMessageTb4b4b4)
Te6edf3channelTff7b72.Td2a8ffadd_message_handlerTb4b4b4(Te6edf3client_message_receivedTb4b4b4)
T8b949e# Inform the user that the server is
T8b949e# connected
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink established with server, enter some text to send, or Tffea00\"Ta5d6ffquitTffea00\"Ta5d6ff to quitTa5d6ff"Tb4b4b4)
T8b949e# When a link is closed, we'll inform the
T8b949e# user, and exit the program
Tff7b72def Td2a8fflink_closedTb4b4b4(Te6edf3linkTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffTIMEOUTTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link timed out, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elif Te6edf3linkTff7b72.Td2a8ffteardown_reason Tff7b72== Te6edf3RNSTff7b72.Td2a8ffLinkTff7b72.Td2a8ffDESTINATION_CLOSEDTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffThe link was closed by the server, exiting nowTa5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffLink closed, exiting nowTa5d6ff"Tb4b4b4)
Te6edf3timeTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff1.5Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
T8b949e# When a packet is received over the channel, we
T8b949e# simply print out the data.
Tff7b72def Td2a8ffclient_message_receivedTb4b4b4(Te6edf3messageTb4b4b4)Tb4b4b4:
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3messageTb4b4b4, Te6edf3StringMessageTb4b4b4)Tb4b4b4:
Te6edf3RNSTff7b72.Td2a8fflogTb4b4b4(Ta5d6ff"Ta5d6ffReceived data on the link: Ta5d6ff" Tff7b72+ Te6edf3messageTff7b72.Td2a8ffdata Tff7b72+ Ta5d6ff"Ta5d6ff (message created at Ta5d6ff" Tff7b72+ Tffa657strTb4b4b4(Te6edf3messageTff7b72.Td2a8fftimestampTb4b4b4) Tff7b72+ Ta5d6ff"Ta5d6ff)Ta5d6ff"Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff> Ta5d6ff"Tb4b4b4, Te6edf3endTff7b72=Ta5d6ff"Ta5d6ff Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffstdoutTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
T8b949e##########################################################
T8b949e#### Program Startup #####################################
T8b949e##########################################################
T8b949e# This part of the program runs at startup,
T8b949e# and parses input of from the user, and then
T8b949e# starts up the desired program mode.
Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3parser Tff7b72= Te6edf3argparseTff7b72.Td2a8ffArgumentParserTb4b4b4(Te6edf3descriptionTff7b72=Ta5d6ff"Ta5d6ffSimple channel exampleTa5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff-sTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff--serverTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstore_trueTa5d6ff"Tb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffwait for incoming link requests from clientsTa5d6ff"
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ff--configTa5d6ff"Tb4b4b4,
Te6edf3actionTff7b72=Ta5d6ff"Ta5d6ffstoreTa5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffpath to alternative Reticulum config directoryTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffadd_argumentTb4b4b4(
Ta5d6ff"Ta5d6ffdestinationTa5d6ff"Tb4b4b4,
Te6edf3nargsTff7b72=Ta5d6ff"Ta5d6ff?Ta5d6ff"Tb4b4b4,
Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3helpTff7b72=Ta5d6ff"Ta5d6ffhexadecimal hash of the server destinationTa5d6ff"Tb4b4b4,
Tffa657typeTff7b72=Tffa657str
Tb4b4b4)
Te6edf3args Tff7b72= Te6edf3parserTff7b72.Td2a8ffparse_argsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3argsTff7b72.Td2a8ffconfigTb4b4b4:
Te6edf3configarg Tff7b72= Te6edf3argsTff7b72.Td2a8ffconfig
Tff7b72elseTb4b4b4:
Te6edf3configarg Tff7b72= Tff7b72None
Tff7b72if Te6edf3argsTff7b72.Td2a8ffserverTb4b4b4:
Te6edf3serverTb4b4b4(Te6edf3configargTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72if Tb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestination Tff7b72== Tff7b72NoneTb4b4b4)Tb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3parserTff7b72.Td2a8ffprint_helpTb4b4b4(Tb4b4b4)
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Tff7b72elseTb4b4b4:
Te6edf3clientTb4b4b4(Te6edf3argsTff7b72.Td2a8ffdestinationTb4b4b4, Te6edf3configargTb4b4b4)
Tff7b72except Tf85149KeyboardInterruptTb4b4b4:
Tffa657printTb4b4b4(Ta5d6ff"Ta5d6ff"Tb4b4b4)
Te6edf3sysTff7b72.Td2a8ffexitTb4b4b4(T79c0ff0Tb4b4b4)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────